package Engine.RMI
{
    import Engine.flash.*;
    import Framework.Holder.*;
    import Framework.MQ.*;
    import Framework.Protocol.*;
    import Framework.Protocol.CDFProtocol.*;
    import Framework.Serialize.*;
    import Framework.Util.*;
    import flash.events.*;
    import flash.net.*;
    import flash.utils.*;

    public class RMIConnection extends EventSender implements IIOHandler, IBusinessHandler
    {
        private var _socket:Socket;
        private var _protocol:IProtocol;
        private var _session:Session;
        private var _timeOutCount:int;
        private var _timer:Timer;
        private var _delay:int = 10000;
        private var _keepActionBack:AMI_IKeepActive_keepActive;

        public function RMIConnection(protocol:IProtocol)
        {
            if (protocol == null)
            {
                this._protocol = new Protocol();
            }
            else
            {
                this._protocol = protocol;
            }
            this._socket = new Socket();
            this._socket.addEventListener(IOErrorEvent.IO_ERROR, this.ioErrorHandler);
            this._socket.addEventListener(Event.CONNECT, this.connectHandler);
            this._socket.addEventListener(Event.CLOSE, this.closeHandler);
            this._socket.addEventListener(SecurityErrorEvent.SECURITY_ERROR, this.securityErrorHandler);
            this._socket.addEventListener(ProgressEvent.SOCKET_DATA, this.socketDataHandler);
            this._timer = new Timer(this._delay);
            this._timer.addEventListener(TimerEvent.TIMER, this.onTimerHandler);
            this._timeOutCount = 0;
            this._keepActionBack = new AMI_IKeepActive_keepActive();
        }

        private function onTimerHandler(event:TimerEvent) : void
        {
            if (this._session && this._session.communicator && this._session.communicator.connected)
            {
                _timeOutCount = _timeOutCount + 1;
                if (++this._timeOutCount == 2)
                {
                    this._session.keepActivePrx.keepActive_async(this._keepActionBack);
                }
            }
        }

        public function get session() : Session
        {
            return this._session;
        }

        public function set session(session:Session) : void
        {
            this._session = session;
        }

        private function ioErrorHandler(event:IOErrorEvent) : void
        {
            trace(event.toString());
            var eventInfo:String = event.text;
            this.close();
            if (this._session.communicator.sessionEvent)
            {
                this._session.communicator.sessionEvent.onAbandon(this._session);
            }
            this.processException(eventInfo, RMIException.ExceptionCodeConnectionWrite);
        }

        private function connectHandler(event:Event) : void
        {
            var rmiObjBind:RMIObjectBind = null;
            var exception:Exception = null;
            this._timer.start();
            this._timeOutCount = 0;
            dispatchEvent(new Event(Event.CONNECT));
            this._session.connection = this;
            this._session.communicator.connecting = false;
            this._session.communicator.connected = true;
            var holder:ArrayHolder = new ArrayHolder();
            this._session.pinkMessage(holder);
            var i:int = 0;
            while (i < holder.value.length)
            {
                
                rmiObjBind = holder.value[i] as RMIObjectBind;
                if (rmiObjBind.outgoingStatus == RMIObjectBind.EOutGoingWaitToSend)
                {
                    if (this.send(rmiObjBind.buffer.byteArray) == -1)
                    {
                        if (rmiObjBind.rmiObject)
                        {
                            this._session.removeBackObject(rmiObjBind.messageId);
                            exception = new RMIException("RMIConnection:send fail", RMIException.ExceptionCodeConnectionWrite);
                            rmiObjBind.rmiObject.cdeException(exception);
                        }
                    }
                }
                i++;
            }
        }

        private function closeHandler(event:Event) : void
        {
            dispatchEvent(new Event(Event.CLOSE));
            trace("RMIConnection closeHandler");
            this.close();
            if (this._session.communicator.sessionEvent)
            {
                this._session.communicator.sessionEvent.onAbandon(this._session);
            }
            this.processException("Connected Close", RMIException.ExceptionCodeConnectionClosed);
            this._protocol.clear();
        }

        private function securityErrorHandler(event:SecurityErrorEvent) : void
        {
            trace("RMIConnection:" + event.text);
            var message:* = this._session.communicator.url + ":" + event.text;
            this._session.connection = null;
            this._session.communicator.connecting = false;
            this._session.communicator.connected = false;
            if (this._session.communicator.sessionEvent)
            {
                this._session.communicator.sessionEvent.onAbandon(this._session);
            }
            this.processException(message, RMIException.ExceptionCodeConnectionNotConnect);
        }

        private function socketDataHandler(event:ProgressEvent) : void
        {
            var bytes:* = new ByteArray();
            this._socket.readBytes(bytes);
            if (this._protocol.handleData(bytes, this, this, null) == -1)
            {
                throw new Error("网络数据处理失败");
            }
        }

        public function connect(ip:String, port:int) : void
        {
            this._session.communicator.connecting = true;
            this._socket.connect(ip, port);
        }

        public function close() : void
        {
            this._socket.close();
            this._timer.removeEventListener(TimerEvent.TIMER, this.onTimerHandler);
            this._timer.stop();
            this._session.connection = null;
            this._session.communicator.connecting = false;
            this._session.communicator.connected = false;
            this._timeOutCount = 0;
        }

        public function send(byte:ByteArray) : int
        {
            return this._protocol.sendDataEncrypt(byte, this) ? (0) : (-1);
        }

        public function sendData(inBuf:ByteArray) : int
        {
            try
            {
                this._timeOutCount = 0;
                this._socket.writeBytes(inBuf, 0, inBuf.length);
                this._socket.flush();
            }
            catch (ex:Error)
            {
                return -1;
            }
            return 0;
        }

        public function handlePacket(inBuf:ByteArray, handler:IBusinessHandler) : Boolean
        {
            var call:SRMICall;
            var rmiObject:RMIObject;
            var context:Context;
            var outStream:SerializeStream;
            var dispatchStatus:ERMIDispatchStatus;
            var __os:SerializeStream;
            var callRet:SRMIReturn;
            var callRet1:SRMIReturn;
            var rmiObjectBind:RMIObjectBind;
            var context1:Context;
            var mb:MessageBlock;
            var isStream:* = new SerializeStream();
            isStream.byteArray = inBuf;
            var messageType:* = ERMIMessageType.__read(isStream);
            if (messageType.equals(ERMIMessageType.MessageTypeCall))
            {
                call = new SRMICall();
                call.__read(isStream);
                rmiObject = this._session.findObject(call.identity);
                if (rmiObject == null)
                {
                    trace(call.identity.name + " object not find");
                    return true;
                }
                context = new Context();
                context._session = this._session;
                context._connection = this;
                context._messageId = call.messageId;
                context._dispachStatus = ERMIDispatchStatus.DispatchOK;
                outStream = new SerializeStream();
                try
                {
                    dispatchStatus = rmiObject.__dispatch(context, call, isStream, outStream);
                }
                catch (ex:Exception)
                {
                    trace(ex.message);
                    outStream.clear();
                    outStream.writeException(ex);
                    dispatchStatus = ERMIDispatchStatus.DispatchException;
                }
                if (call.messageId == 0 || dispatchStatus.equals(ERMIDispatchStatus.DispatchAsync))
                {
                    return true;
                }
                __os = new SerializeStream();
                ERMIMessageType.MessageTypeCallRet.__write(__os);
                callRet = new SRMIReturn();
                callRet.messageId = call.messageId;
                callRet.dispatchStatus = dispatchStatus;
                callRet.__write(__os);
                __os.writeByteArray(outStream);
                return this.send(__os.byteArray) != -1;
            }
            else if (messageType.equals(ERMIMessageType.MessageTypeCallRet))
            {
                callRet1 = new SRMIReturn();
                callRet1.__read(isStream);
                rmiObjectBind = this._session.findRemoveBackObject(callRet1.messageId);
                if (rmiObjectBind == null)
                {
                    trace(callRet1.messageId + " message id not find");
                    return true;
                }
                context1 = new Context();
                context1._session = this._session;
                context1._connection = this;
                context1._messageId = callRet1.messageId;
                context1._dispachStatus = callRet1.dispatchStatus;
                rmiObjectBind.__back(context1, isStream);
            }
            else if (messageType.equals(ERMIMessageType.MessageTypeMQ))
            {
                mb = new MessageBlock();
                mb.__read(isStream);
                if (this._session.messageHandler)
                {
                    this._session.messageHandler.onMessage(mb);
                }
            }
            return true;
        }

        private function processException(message:String, code:int) : void
        {
            var rmiObjBind:RMIObjectBind = null;
            var holder:ArrayHolder = new ArrayHolder();
            this._session.pinkRemoveMessage(holder);
            var exception:Exception = new Exception(message, code);
            var i:int = 0;
            while (i < holder.value.length)
            {
                
                rmiObjBind = holder.value[i] as RMIObjectBind;
                if (rmiObjBind != null)
                {
                    if (this._session.communicator.prepareCommandHandler)
                    {
                        this._session.communicator.prepareCommandHandler.prepareBackException(rmiObjBind.rmiObject, exception);
                    }
                    rmiObjBind.rmiObject.cdeException(exception);
                }
                i++;
            }
        }

    }
}
